/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "local-include/reg.h"
#include <cpu/cpu.h>
#include <cpu/ifetch.h>
#include <cpu/decode.h>
#include <cpu/difftest.h>
#define R(i) gpr(i)
#define Mr vaddr_read
#define Mw vaddr_write

enum {
  TYPE_I, TYPE_U, TYPE_S,TYPE_J,TYPE_R,TYPE_B,
  TYPE_N, // none
};

#define src1R() do { *src1 = R(rs1); } while (0)
#define src2R() do { *src2 = R(rs2); } while (0)
#define immI() do { *imm = SEXT(BITS(i, 31, 20), 12); } while(0)
#define immU() do { *imm = SEXT(BITS(i, 31, 12), 20) << 12; } while(0)
#define immS() do { *imm = (SEXT(BITS(i, 31, 25), 7) << 5) | BITS(i, 11, 7); } while(0)
#define immJ() do { *imm = (SEXT(BITS(i, 31, 31), 1) << 19) | (BITS(i, 19, 12) << 12) | (BITS(i, 20, 20) << 11) | (BITS(i, 30, 21) << 1); } while(0)
#define immB() do { *imm = (SEXT(BITS(i, 31, 31), 1) << 12) | (BITS(i,7,7) << 11) | (BITS(i,30,25) << 5) | (BITS(i,11,8) << 1);} while(0)
#define CSRSW() do { if(BITS(s->isa.inst.val, 19, 15) != 0){CSRW(imm, CSR(imm) | (uint32_t)src1);} } while(0)
int sra(int x,int n){
  int sign_extension = (x < 0) ? (~0 << (sizeof(int) * 8 - 1)) : 0;
    int shifted_value = (x >> n) | sign_extension;
    return shifted_value;
}
int blanks = 0;
void ftrace_log(vaddr_t pc, vaddr_t dnpc, uint32_t inst, int inst_type){
  extern Elf32_Shdr symbol_table_section;
  extern Elf32_Shdr string_table_section;
  extern FILE* elf_fp;
  int type;
  size_t temp;
  Elf32_Sym symbol_table_entry;
  fseek(elf_fp, symbol_table_section.sh_offset, SEEK_SET);
  if(inst_type == TYPE_J){
    type = 1; // call
  }else{
    if(BITS(inst, 19, 15) == 1 && SEXT(BITS(inst, 31, 20), 12) == 0){
      type = 0; // ret
    }else{
      type = 1; // call
    }
  }
  for (int i = 0; i < symbol_table_section.sh_size / sizeof(Elf32_Sym); i++) {
      temp = fread(&symbol_table_entry, sizeof(Elf32_Sym), 1, elf_fp);
      if(temp != 1){
        assert(0);
      }
      // Log("The sym info is %d",ELF32_ST_TYPE(symbol_table_entry.st_info));
      if(ELF32_ST_TYPE(symbol_table_entry.st_info) == STT_FUNC){
        // judge whether the address is func call
        if(dnpc >= symbol_table_entry.st_value && dnpc < symbol_table_entry.st_value + symbol_table_entry.st_size){
          // Print the symbol name and value
          char symbol_name[256];
          fseek(elf_fp, string_table_section.sh_offset + symbol_table_entry.st_name, SEEK_SET);
          temp = fread(symbol_name, sizeof(char), sizeof(symbol_name) - 1, elf_fp);
          symbol_name[sizeof(symbol_name) - 1] = '\0'; 
          log_write("%x: ",pc);
          if(type){
            for(int i=0; i < blanks; i++){
              log_write("\t");
            }
            log_write("call [%s@%x]\n",symbol_name,dnpc);
            blanks++;
          }else{
            blanks--;
            for(int i=0; i < blanks; i++){
              log_write("\t");
            }
            log_write("ret  [%s]\n",symbol_name);
            blanks--;
          }
          
        }
      }
      
      // printf("Symbol: %s, Value: 0x%x, Size: 0x%x\n", symbol_name, symbol_table_entry.st_value, symbol_table_entry.st_size);
      fseek(elf_fp, symbol_table_section.sh_offset + i*sizeof(Elf32_Sym), SEEK_SET);
  }
}


static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, word_t *imm, int type) {
  uint32_t i = s->isa.inst.val;
  int rs1 = BITS(i, 19, 15);
  int rs2 = BITS(i, 24, 20);
  *rd     = BITS(i, 11, 7);
  switch (type) {
    case TYPE_I: src1R();          immI(); break;
    case TYPE_U:                   immU(); break;
    case TYPE_S: src1R(); src2R(); immS(); break;
    case TYPE_J:                   immJ(); break;
    case TYPE_R: src1R(); src2R();         break;
    case TYPE_B: src1R(); src2R(); immB(); break;
  }
}

static int decode_exec(Decode *s) {
  int rd = 0;
  word_t src1 = 0, src2 = 0, imm = 0;
  s->dnpc = s->snpc;

#define INSTPAT_INST(s) ((s)->isa.inst.val)
#define INSTPAT_MATCH(s, name, type, ... /* execute body */ ) { \
  decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \
  __VA_ARGS__ ; \
}

  INSTPAT_START();
  INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui    , U, R(rd) = imm);
  INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc  , U, R(rd) = s->pc + imm );
  INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal    , J, R(rd) = s->pc + 4;s->dnpc = s->pc + imm;IFDEF(CONFIG_FTRACE, ftrace_log(s->pc,s->dnpc,s->isa.inst.val,TYPE_J)));
  INSTPAT("??????? ????? ????? 000 ????? 11001 11", jalr   , I, R(rd) = s->pc + 4;s->dnpc = (src1 + imm) & (~1);IFDEF(CONFIG_FTRACE, ftrace_log(s->pc,s->dnpc,s->isa.inst.val,TYPE_I)));
  INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add    , R, R(rd) = src1 + src2);
  INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub    , R, R(rd) = src1 - src2);
  INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu  , I, R(rd) = src1 < imm ? 1 : 0);
  INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq    , B, s->dnpc = src1 == src2 ? s->pc + imm : s->dnpc);
  INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne    , B, s->dnpc = src1 != src2 ? s->pc + imm : s->dnpc);
  INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt    , B, s->dnpc = (int)src1 < (int)src2 ? s->pc + imm : s->dnpc);
  INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge    , B, s->dnpc = (int)src1 >= (int)src2 ? s->pc + imm : s->dnpc);
  INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu   , B, s->dnpc = src1 >= src2 ? s->pc + imm : s->dnpc);
  INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu   , B, s->dnpc = src1 < src2 ? s->pc + imm : s->dnpc);
  INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh     , S, Mw(src1 + imm, 2, src2));
  INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb     , S, Mw(src1 + imm, 1, src2));
  INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai   , I, R(rd) = sra(src1,BITS(imm,4,0)));
  INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli   , I, R(rd) = src1 << (BITS(imm,4,0)));
  INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli   , I, R(rd) = src1 >> (BITS(imm,4,0)));
  INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb     , I, R(rd) = (int32_t)(int8_t)Mr(src1 + imm, 1));
  INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh     , I, R(rd) = (int32_t)(int16_t)Mr(src1 + imm, 2));
  INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu    , I, R(rd) = Mr(src1 + imm, 1));
  INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu    , I, R(rd) = Mr(src1 + imm, 2));
  INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi   , I, R(rd) = src1 & imm);
  INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll    , R, R(rd) = src1 << src2);
  INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt    , R, R(rd) = (int)src1 < (int)src2 ? 1 : 0);
  INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl    , R, R(rd) = src1 >> src2);
  INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra    , R, R(rd) = sra(src1,src2));
  INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and    , R, R(rd) = src1 & src2);
  INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or     , R, R(rd) = src1 | src2);
  INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor    , R, R(rd) = src1 ^ src2);
  INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu   , R, R(rd) = src1 < src2 ? 1 : 0);
  INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori   , I, R(rd) = imm ^ src1);
  INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori    , I, R(rd) = imm | src1);
  INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem    , R, R(rd) = (int)src1 % (int)src2);
  INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu   , R, R(rd) = src1 % src2);
  INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul    , R, R(rd) = (int32_t)src1 * (int32_t)src2);
  INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh   , R, R(rd) = (int32_t)((SEXT((int64_t)src1,32) * SEXT((int64_t)src2,32)) >> 32));
  INSTPAT("0000001 ????? ????? 011 ????? 01100 11", mulhu  , R, R(rd) = (((uint64_t)src1 * src2) >> 32));
  INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div    , R, R(rd) = (int)src1 / (int)src2);
  INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu   , R, R(rd) = src1 / src2);
  INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw     , I, R(rd) = Mr(src1 + imm, 4));
  INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw     , S, Mw(src1 + imm, 4, src2));
  INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi   , I, R(rd) = imm + src1);
  INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0
  INSTPAT("0000000 00000 00000 000 00000 11100 11", ecall  , N, s->dnpc = isa_raise_intr(R(17),s->pc););
  INSTPAT("0011000 00010 00000 000 00000 11100 11", mret   , I, s->dnpc = CSR(0x341));
  INSTPAT("??????? ????? ????? 001 ????? 11100 11", csrrw  , I, R(rd) = CSR(imm); CSRW(imm, src1););
  INSTPAT("??????? ????? ????? 010 ????? 11100 11", csrrs  , I, difftest_skip_ref(); ;R(rd) = CSR(imm); CSRW(imm, CSR(imm) | src1));
  INSTPAT("??????? ????? ????? 011 ????? 11100 11", csrrc  , I, R(rd) = CSR(imm); CSRW(imm, CSR(imm) ^ src1));
  // INSTPAT("??????? ????? ????? 101 ????? 11100 11", csrrwi , I,)
  // INSTPAT("??????? ????? ????? 110 ????? 11100 11", csrrsi , I,)
  // INSTPAT("??????? ????? ????? 111 ????? 11100 11", csrrci , I,)
  INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv    , N, INV(s->pc));
  INSTPAT_END();

  R(0) = 0; // reset $zero to 0

  return 0;
}

int isa_exec_once(Decode *s) {
  s->isa.inst.val = inst_fetch(&s->snpc, 4);
  return decode_exec(s);
}
